package io.gitee.zhangbinhub.admin.component

import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.acp.core.common.task.BaseAsyncTask
import io.gitee.zhangbinhub.acp.core.common.task.threadpool.ThreadPoolService
import io.gitee.zhangbinhub.admin.base.PendingCreatedNotify
import io.gitee.zhangbinhub.admin.base.PendingFinishedNotify
import io.gitee.zhangbinhub.admin.conf.WorkFlowCustomerConfiguration
import io.gitee.zhangbinhub.admin.repo.RoleRepository
import org.camunda.bpm.engine.RepositoryService
import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity
import org.camunda.bpm.engine.task.IdentityLink
import org.camunda.bpm.engine.task.IdentityLinkType
import org.camunda.bpm.engine.task.Task
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component

@Component
class WorkFlowListener(
    private val logAdapter: LogAdapter,
    private val roleRepository: RoleRepository,
    private val workFlowCustomerConfiguration: WorkFlowCustomerConfiguration,
    private val pendingCreatedNotifyList: List<PendingCreatedNotify>,
    private val pendingFinishedNotifyList: List<PendingFinishedNotify>,
    private val repositoryService: RepositoryService
) {
    fun getIdentityLinkUserId(identityLinkList: List<IdentityLink>): List<String> =
        identityLinkList.filter { identityLink ->
            identityLink.type == IdentityLinkType.ASSIGNEE && !CommonTools.isNullStr(identityLink.userId)
        }.map { identityLink -> identityLink.userId }.ifEmpty {
            identityLinkList.filter { identityLink ->
                identityLink.type == IdentityLinkType.CANDIDATE
            }.flatMap { identityLink ->
                mutableListOf<String>().apply {
                    if (!CommonTools.isNullStr(identityLink.userId)) {
                        this.add(identityLink.userId)
                    }
                    if (!CommonTools.isNullStr(identityLink.groupId)) {
                        this.addAll(roleRepository.findAllByCodeInOrderBySortAsc(listOf(identityLink.groupId))
                            .flatMap { role ->
                                role.userSet
                            }.map { user ->
                                if (!CommonTools.isNullStr(user.id)) {
                                    user.id
                                } else {
                                    ""
                                }
                            })
                    }
                }
            }
        }.toSet().filter { userId -> !CommonTools.isNullStr(userId) }.toList()

    @EventListener
    fun onTaskEvent(taskEntity: TaskEntity) {
        try {
            logAdapter.debug("收到工作流任务【${taskEntity.id}】事件【${taskEntity.eventName}】")
            if (taskEntity.eventName == "create") {
                notifyPendingCreated(
                    taskEntity.processInstance.businessKey, taskEntity,
                    if (CommonTools.isNullStr(taskEntity.assignee)) {
                        getIdentityLinkUserId(taskEntity.identityLinks)
                    } else {
                        listOf(taskEntity.assignee)
                    }
                )
            }
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
        }
    }

    /**
     * 生成待办通知
     * @param businessKey 业务键
     * @param task 任务
     * @param userIdList 用户ID，默认null；当用户ID为null时，自动取运行时任务所有待办人员
     */
    @Throws(WebException::class)
    fun notifyPendingCreated(businessKey: String, task: Task, userIdList: List<String>) {
        if (workFlowCustomerConfiguration.notifyPendingCreated) {
            logAdapter.debug("进行待办通知...")
            logAdapter.debug("userIdList --> $userIdList")
            repositoryService.createProcessDefinitionQuery().processDefinitionId(task.processDefinitionId)
                .singleResult()?.also { processDefinition ->
                    if (userIdList.isNotEmpty()) {
                        logAdapter.debug("开始待办通知处理【${processDefinition.key}】【${task.processInstanceId}】【$businessKey】，通知处理器数量【${pendingCreatedNotifyList.size}】...")
                        logAdapter.debug("处理器：${pendingCreatedNotifyList}")
                        pendingCreatedNotifyList.forEach { handle ->
                            ThreadPoolService.getInstance(5, 5, Int.MAX_VALUE, "create-pending-pool")
                                .addTask(object : BaseAsyncTask("create-pending") {
                                    override fun afterExecuteFun(result: Any) {}
                                    override fun beforeExecuteFun(): Boolean = true
                                    override fun executeFun(): Any {
                                        handle.doTaskNotify(businessKey, task, processDefinition, userIdList)
                                        return true
                                    }
                                })
                        }
                    }
                } ?: throw WebException("获取流程实例失败：taskId【${task.id}】")
        }
    }

    /**
     * 完成待办通知
     * @param businessKey 业务键
     * @param task 任务
     * @param userIdList 用户ID，默认null；当用户ID为null时，自动取任务所有待办人员
     */
    @Throws(WebException::class)
    fun notifyPendingFinished(businessKey: String, task: Task, userIdList: List<String>) {
        if (workFlowCustomerConfiguration.notifyPendingFinished) {
            logAdapter.debug("删除待办通知...")
            logAdapter.debug("userIdList --> $userIdList")
            repositoryService.createProcessDefinitionQuery().processDefinitionId(task.processDefinitionId)
                .singleResult()?.also { processDefinition ->
                    if (userIdList.isNotEmpty()) {
                        logAdapter.debug("开始删除待办通知处理【${processDefinition.key}】【${task.processInstanceId}】【$businessKey】，通知处理器数量【${pendingFinishedNotifyList.size}】...")
                        logAdapter.debug("处理器：${pendingFinishedNotifyList}")
                        pendingFinishedNotifyList.forEach { handle ->
                            ThreadPoolService.getInstance(5, 5, Int.MAX_VALUE, "delete-pending-pool")
                                .addTask(object : BaseAsyncTask("delete-pending") {
                                    override fun afterExecuteFun(result: Any) {}
                                    override fun beforeExecuteFun(): Boolean = true
                                    override fun executeFun(): Any {
                                        handle.doTaskNotify(businessKey, task, processDefinition, userIdList)
                                        return true
                                    }
                                })
                        }
                    }
                } ?: throw WebException("获取流程实例失败：taskId【${task.id}】")
        }
    }
}